/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util;
import java.beans.*;
import java.lang.ref.*;
import java.util.EventListener;
import java.awt.event.FocusListener;
import java.awt.event.FocusEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import org.openide.filesystems.*;
import org.openide.loaders.OperationListener;
import org.openide.loaders.OperationEvent;
import org.openide.nodes.*;
/** Property change listener that delegates to another one
* change listener but hold only weak reference to that
* listener, so it does not prevent it to be finalized.
*
* @author Jaroslav Tulach
*/
public abstract class WeakListener extends Object implements java.util.EventListener {
/** queue with all weak references */
private static ReferenceQueue QUEUE = new ReferenceQueue ();
/** how often clean */
private static int CLEANER_TIME = 25000;
/** clearner task */
private static RequestProcessor.Task CLEANER_TASK = RequestProcessor.postRequest(
new Cleaner (), CLEANER_TIME, Thread.MIN_PRIORITY
);
/** weak reference to listener */
private Reference ref;
/** class of the listener */
Class listenerClass;
/** weak reference to source */
private Reference source;
/**
* @param listenerClass class of the listener
* @param l listener to delegate to
*/
protected WeakListener (Class listenerClass, java.util.EventListener l) {
this.listenerClass = listenerClass;
ref = new ListenerReference (l, this);
}
/** Setter for source
*/
final void setSource (Object source) {
if (source == null) {
this.source = null;
} else {
this.source = new WeakReference (source);
}
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected abstract String removeMethodName ();
/** Getter for the target listener.
* @param event the event the we want to distribute
* @return null if there is no listener because it has been finalized
*/
protected final java.util.EventListener get (java.util.EventObject ev) {
if (ref == null) return null;
EventListener l = (EventListener)ref.get ();
if (l != null) {
return l;
} else {
if (ev != null) {
removeListener (ev.getSource ());
ref = null;
}
return null;
}
}
/** Removes a listener if the source field is filled.
*/
private void removeListener () {
Reference s = this.source;
if (s == null) return;
Object src = s.get ();
if (src != null) {
removeListener (src);
}
}
/** Tries to find a removePropertyChangeListener method and invoke it
* @param source the source object
*/
private void removeListener (Object source) {
try {
java.lang.reflect.Method m = source.getClass ().getMethod (
removeMethodName (),
new Class [] { listenerClass }
);
// removePropertyChangeListener (this)
m.invoke (source, new Object[] { this });
} catch (Exception ex) {
// ignore failure
}
}
/** To string.
*/
public String toString () {
java.lang.ref.Reference r = ref;
if (r == null) {
return super.toString () + " " + NbBundle.getBundle(WeakListener.class).getString("MSG_ToNowhere");
} else {
return super.toString () + " "+ NbBundle.getBundle(WeakListener.class).getString("MSG_To") + r.get ();
}
}
//
// Methods for establishing connections
//
/** the implementation of weak listener factory.
*/
private static Factory factory;
static {
try {
Class c = Class.forName ("org.openide.util.WeakListener13"); // NOI18N
factory = (Factory)c.newInstance ();
} catch (Throwable t) {
// use 1.2 version
factory = new WeakListener12 ();
}
}
/** Creates a weak listener. The sample usage code is
* <PRE>
* AnySource source;
* AnyListener listener;
*
* AnyListener weak = WeakListener.any (listener, source);
* source.addAnyListener (weak);
* </PRE>
* @param l the listener to delegate to
* @param source the source that the listener should detach from when listener <CODE>l</CODE>
* is no longer used, can be <CODE>null</CODE>
*/
public static NodeListener node (NodeListener l, Object source) {
return factory.node (l, source);
}
public static PropertyChangeListener propertyChange (PropertyChangeListener l, Object source) {
return factory.propertyChange (l, source);
}
public static VetoableChangeListener vetoableChange (VetoableChangeListener l, Object source) {
return factory.vetoableChange (l, source);
}
public static FileChangeListener fileChange (FileChangeListener l, Object source) {
return factory.fileChange (l, source);
}
public static FileStatusListener fileStatus (FileStatusListener l, Object source) {
return factory.fileStatus (l, source);
}
public static RepositoryListener repository (RepositoryListener l, Object source) {
return factory.repository (l, source);
}
public static DocumentListener document (DocumentListener l, Object source) {
return factory.document (l, source);
}
public static ChangeListener change (ChangeListener l, Object source) {
return factory.change (l, source);
}
public static FocusListener focus (FocusListener l, Object source) {
return factory.focus (l, source);
}
public static OperationListener operation (OperationListener l, Object source) {
return factory.operation (l, source);
}
/** Weak property change listener
* @deprecated use appropriate method instead
*/
public static class PropertyChange extends WeakListener
implements PropertyChangeListener {
/** Constructor.
* @param l listener to delegate to
*/
public PropertyChange (PropertyChangeListener l) {
super (PropertyChangeListener.class, l);
}
/** Constructor.
* @param clazz required class
* @param l listener to delegate to
*/
PropertyChange (Class clazz, PropertyChangeListener l) {
super (clazz, l);
}
/** Tests if the object we reference to still exists and
* if so, delegate to it. Otherwise remove from the source
* if it has removePropertyChangeListener method.
*/
public void propertyChange (PropertyChangeEvent ev) {
PropertyChangeListener l = (PropertyChangeListener)super.get (ev);
if (l != null) l.propertyChange (ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removePropertyChangeListener"; // NOI18N
}
}
/** Weak vetoable change listener
* @deprecated use appropriate method instead
*/
public static class VetoableChange extends WeakListener
implements VetoableChangeListener {
/** Constructor.
* @param l listener to delegate to
*/
public VetoableChange (VetoableChangeListener l) {
super (VetoableChangeListener.class, l);
}
/** Tests if the object we reference to still exists and
* if so, delegate to it. Otherwise remove from the source
* if it has removePropertyChangeListener method.
*/
public void vetoableChange (PropertyChangeEvent ev) throws PropertyVetoException {
VetoableChangeListener l = (VetoableChangeListener)super.get (ev);
if (l != null) l.vetoableChange (ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeVetoableChangeListener"; // NOI18N
}
}
/** Weak file change listener.
* @deprecated use appropriate method instead
*/
public static class FileChange extends WeakListener
implements FileChangeListener {
/** Constructor.
* @param l listener to delegate to
*/
public FileChange (FileChangeListener l) {
super (FileChangeListener.class, l);
}
/** Fired when a new folder has been created. This action can only be
* listened in folders containing the created file up to the root of
* file system.
*
* @param fe the event describing context where action has taken place
*/
public void fileFolderCreated (FileEvent ev) {
FileChangeListener l = (FileChangeListener)super.get (ev);
if (l != null) l.fileFolderCreated (ev);
}
/** Fired when a new file has been created. This action can only be
* listened in folders containing the created file up to the root of
* file system.
*
* @param fe the event describing context where action has taken place
*/
public void fileDataCreated (FileEvent ev) {
FileChangeListener l = (FileChangeListener)super.get (ev);
if (l != null) l.fileDataCreated (ev);
}
/** Fired when a file has been changed.
* @param fe the event describing context where action has taken place
*/
public void fileChanged (FileEvent ev) {
FileChangeListener l = (FileChangeListener)super.get (ev);
if (l != null) l.fileChanged (ev);
}
/** Fired when a file has been deleted.
* @param fe the event describing context where action has taken place
*/
public void fileDeleted (FileEvent ev) {
FileChangeListener l = (FileChangeListener)super.get (ev);
if (l != null) l.fileDeleted (ev);
}
/** Fired when a file has been renamed.
* @param fe the event describing context where action has taken place
* and the original name and extension.
*/
public void fileRenamed (FileRenameEvent ev) {
FileChangeListener l = (FileChangeListener)super.get (ev);
if (l != null) l.fileRenamed (ev);
}
/** Fired when a file attribute has been changed.
* @param fe the event describing context where action has taken place,
* the name of attribute and old and new value.
*/
public void fileAttributeChanged (FileAttributeEvent ev) {
FileChangeListener l = (FileChangeListener)super.get (ev);
if (l != null) l.fileAttributeChanged (ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeFileChangeListener"; // NOI18N
}
}
/** Weak file status listener.
* @deprecated use appropriate method instead
*/
public static class FileStatus extends WeakListener
implements FileStatusListener {
/** Constructor.
*/
public FileStatus (FileStatusListener l) {
super (FileStatusListener.class, l);
}
/** Notifies listener about change in annotataion of a few files.
* @param ev event describing the change
*/
public void annotationChanged(FileStatusEvent ev) {
FileStatusListener l = (FileStatusListener)super.get (ev);
if (l != null) l.annotationChanged (ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeFileStatusListener"; // NOI18N
}
}
/** Weak file system pool listener.
* @deprecated use appropriate method instead
*/
public static class Repository extends WeakListener
implements RepositoryListener {
/** Constructor.
* @param l listener to delegate to
*/
public Repository (RepositoryListener l) {
super (RepositoryListener.class, l);
}
/** Called when new file system is added to the pool.
* @param ev event describing the action
*/
public void fileSystemAdded (RepositoryEvent ev) {
RepositoryListener l = (RepositoryListener)super.get (ev);
if (l != null) l.fileSystemAdded (ev);
}
/** Called when a file system is deleted from the pool.
* @param ev event describing the action
*/
public void fileSystemRemoved (RepositoryEvent ev) {
RepositoryListener l = (RepositoryListener)super.get (ev);
if (l != null) l.fileSystemRemoved (ev);
}
/** Called when a Repository is reordered. */
public void fileSystemPoolReordered(RepositoryReorderedEvent ev) {
RepositoryListener l = (RepositoryListener)super.get (ev);
if (l != null) l.fileSystemPoolReordered (ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeRepositoryListener"; // NOI18N
}
}
/** Weak document modifications listener.
* This class if final only for performance reasons,
* can be happily unfinaled if desired.
* @deprecated use appropriate method instead
*/
public static final class Document extends WeakListener
implements DocumentListener {
/** Constructor.
* @param l listener to delegate to
*/
public Document (final DocumentListener l) {
super (DocumentListener.class, l);
}
/** Gives notification that an attribute or set of attributes changed.
* @param ev event describing the action
*/
public void changedUpdate(DocumentEvent ev) {
final DocumentListener l = docGet(ev);
if (l != null) l.changedUpdate(ev);
}
/** Gives notification that there was an insert into the document.
* @param ev event describing the action
*/
public void insertUpdate(DocumentEvent ev) {
final DocumentListener l = docGet(ev);
if (l != null) l.insertUpdate(ev);
}
/** Gives notification that a portion of the document has been removed.
* @param ev event describing the action
*/
public void removeUpdate(DocumentEvent ev) {
final DocumentListener l = docGet(ev);
if (l != null) l.removeUpdate(ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeDocumentListener"; // NOI18N
}
/** Getter for the target listener.
* @param event the event the we want to distribute
* @return null if there is no listener because it has been finalized
*/
private DocumentListener docGet (DocumentEvent ev) {
if (super.ref == null) return null;
DocumentListener l = (DocumentListener)super.ref.get ();
if (l != null) {
return l;
} else {
super.removeListener (ev.getDocument());
super.ref = null;
return null;
}
}
} // end of Document inner class
/** Weak swing change listener.
* This class if final only for performance reasons,
* can be happily unfinaled if desired.
* @deprecated use appropriate method instead
*/
public static final class Change extends WeakListener
implements ChangeListener {
/** Constructor.
* @param l listener to delegate to
*/
public Change (ChangeListener l) {
super (ChangeListener.class, l);
}
/** Called when new file system is added to the pool.
* @param ev event describing the action
*/
public void stateChanged (final ChangeEvent ev) {
ChangeListener l = (ChangeListener)super.get(ev);
if (l != null) l.stateChanged (ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeChangeListener"; // NOI18N
}
}
/** Weak version of listener for changes in one node.
* This class if final only for performance reasons,
* can be happily unfinaled if desired.
* @deprecated use appropriate method instead
*/
public static final class Node extends WeakListener.PropertyChange
implements NodeListener {
/** Constructor.
* @param l listener to delegate to
*/
public Node (NodeListener l) {
super (NodeListener.class, l);
}
/** Delegates to the original listener.
*/
public void childrenAdded (NodeMemberEvent ev) {
NodeListener l = (NodeListener)super.get (ev);
if (l != null) l.childrenAdded (ev);
}
/** Delegates to the original listener.
*/
public void childrenRemoved (NodeMemberEvent ev) {
NodeListener l = (NodeListener)super.get (ev);
if (l != null) l.childrenRemoved (ev);
}
/** Delegates to the original listener.
*/
public void childrenReordered (NodeReorderEvent ev) {
NodeListener l = (NodeListener)super.get (ev);
if (l != null) l.childrenReordered (ev);
}
/** Delegates to the original listener.
*/
public void nodeDestroyed (NodeEvent ev) {
NodeListener l = (NodeListener)super.get (ev);
if (l != null) l.nodeDestroyed (ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeNodeListener"; // NOI18N
}
}
/** Weak version of focus listener.
* This class if final only for performance reasons,
* can be happily unfinaled if desired.
* @deprecated use appropriate method instead
*/
public static final class Focus extends WeakListener
implements FocusListener {
/** Constructor.
* @param l listener to delegate to
*/
public Focus (FocusListener l) {
super (FocusListener.class, l);
}
/** Delegates to the original listener.
*/
public void focusGained(FocusEvent ev) {
FocusListener l = (FocusListener)super.get (ev);
if (l != null) l.focusGained (ev);
}
/** Delegates to the original listener.
*/
public void focusLost(FocusEvent ev) {
FocusListener l = (FocusListener)super.get (ev);
if (l != null) l.focusLost (ev);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeFocusListener"; // NOI18N
}
}
/** Weak property change listener
*/
final static class Operation extends WeakListener
implements OperationListener {
/** Constructor.
* @param l listener to delegate to
*/
public Operation (OperationListener l) {
super (PropertyChangeListener.class, l);
}
/** Method name to use for removing the listener.
* @return name of method of the source object that should be used
* to remove the listener from listening on source of events
*/
protected String removeMethodName () {
return "removeOperationListener"; // NOI18N
}
/** Object has been recognized by
* {@link DataLoaderPool#findDataObject}.
* This allows listeners
* to attach additional cookies, etc.
*
* @param ev event describing the action
*/
public void operationPostCreate(OperationEvent ev) {
OperationListener l = (OperationListener)super.get (ev);
if (l != null) l.operationPostCreate (ev);
}
/** Object has been successfully copied.
* @param ev event describing the action
*/
public void operationCopy(OperationEvent.Copy ev) {
OperationListener l = (OperationListener)super.get (ev);
if (l != null) l.operationCopy (ev);
}
/** Object has been successfully moved.
* @param ev event describing the action
*/
public void operationMove(OperationEvent.Move ev) {
OperationListener l = (OperationListener)super.get (ev);
if (l != null) l.operationMove (ev);
}
/** Object has been successfully deleted.
* @param ev event describing the action
*/
public void operationDelete(OperationEvent ev) {
OperationListener l = (OperationListener)super.get (ev);
if (l != null) l.operationDelete (ev);
}
/** Object has been successfully renamed.
* @param ev event describing the action
*/
public void operationRename(OperationEvent.Rename ev) {
OperationListener l = (OperationListener)super.get (ev);
if (l != null) l.operationRename (ev);
}
/** A shadow of a data object has been created.
* @param ev event describing the action
*/
public void operationCreateShadow (OperationEvent.Copy ev) {
OperationListener l = (OperationListener)super.get (ev);
if (l != null) l.operationCreateShadow (ev);
}
/** New instance of an object has been created.
* @param ev event describing the action
*/
public void operationCreateFromTemplate(OperationEvent.Copy ev) {
OperationListener l = (OperationListener)super.get (ev);
if (l != null) l.operationCreateFromTemplate (ev);
}
}
/** Reference that also holds ref to WeakListener.
*/
private static final class ListenerReference extends WeakReference {
final WeakListener weakListener;
public ListenerReference (
Object ref,
WeakListener weakListener
) {
super (ref, QUEUE);
this.weakListener = weakListener;
}
}
/** Class that periodically runs cleaning of the queue.
*/
private static final class Cleaner extends Object implements Runnable {
public void run () {
for (;;) {
ListenerReference lr = (ListenerReference)QUEUE.poll();
if (lr == null) break;
lr.weakListener.removeListener ();
}
CLEANER_TASK.schedule (CLEANER_TIME);
}
}
/** Factory for 1.2 or 1.3 implementation.
*/
static interface Factory {
public NodeListener node (NodeListener l, Object source);
public PropertyChangeListener propertyChange (PropertyChangeListener l, Object source);
public VetoableChangeListener vetoableChange (VetoableChangeListener l, Object source);
public FileChangeListener fileChange (FileChangeListener l, Object source);
public FileStatusListener fileStatus (FileStatusListener l, Object source);
public RepositoryListener repository (RepositoryListener l, Object source);
public DocumentListener document (DocumentListener l, Object source);
public ChangeListener change (ChangeListener l, Object source);
public FocusListener focus (FocusListener l, Object source);
public OperationListener operation (OperationListener l, Object source);
}
}
/*
* Log
* 21 Gandalf 1.20 1/12/00 Pavel Buzek I18N
* 20 Gandalf 1.19 1/5/00 Jaroslav Tulach Added operation listener.
* 19 Gandalf 1.18 11/5/99 Jaroslav Tulach 1.3 works better.
* 18 Gandalf 1.17 11/5/99 Jaroslav Tulach WeakListener has now
* registration methods.
* 17 Gandalf 1.16 11/4/99 Jaroslav Tulach nodes (..., ...);
* 16 Gandalf 1.15 10/29/99 Jaroslav Tulach MultiFileSystem +
* FileStatusEvent
* 15 Gandalf 1.14 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 14 Gandalf 1.13 9/30/99 Jaroslav Tulach OpenSupport is attached
* to setValid veto change of its data object.
* 13 Gandalf 1.12 9/17/99 Jaroslav Tulach toString changed.
* 12 Gandalf 1.11 8/27/99 Jaroslav Tulach New threading model &
* Children.
* 11 Gandalf 1.10 7/11/99 David Simonek window system change...
* 10 Gandalf 1.9 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 9 Gandalf 1.8 3/27/99 David Simonek
* 8 Gandalf 1.7 3/26/99 David Simonek Focus added
* 7 Gandalf 1.6 2/19/99 Petr Hamernik
* 6 Gandalf 1.5 2/11/99 Ian Formanek Renamed FileSystemPool ->
* Repository
* 5 Gandalf 1.4 2/5/99 Jaroslav Tulach NodeListener
* 4 Gandalf 1.3 2/4/99 Jaroslav Tulach Properties and explorer
* 3 Gandalf 1.2 1/13/99 David Simonek
* 2 Gandalf 1.1 1/12/99 Jaroslav Tulach Modules are loaded by
* URLClassLoader
* 1 Gandalf 1.0 1/5/99 Ian Formanek
* $
*/